// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "SmartMovieSceneSequenceExtensions.h"

#include "CineCameraActor.h"
#include "CineCameraComponent.h"
#include "LevelSequence.h"
#include "MovieSceneSequence.h"
#include "MovieScene.h"
#include "MovieSceneToolsUserSettings.h"
#include "SequencerBindingProxy.h"
#include "Algo/Find.h"
#include "Tracks/MovieScene3DTransformTrack.h"


TMap<int32, FGuid> USmartMovieSceneSequenceExtensions::SmGetAllSpawnableGuid(UMovieSceneSequence * Sequence)
{
	TMap<int32, FGuid> Result;
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		const int32 Count = MovieScene->GetSpawnableCount();
		if (Count > 0) {
			for (int32 Index = 0; Index <= Count - 1; Index++) {
				const FGuid Guid = MovieScene->GetSpawnable(Index).GetGuid();
				Result.Add(Index, FGuid(Guid));
			}
		}
	}
	return Result;
}

TMap<int32, FString> USmartMovieSceneSequenceExtensions::SmGetAllSpawnableName(UMovieSceneSequence * Sequence)
{
	TMap<int32, FString> Result;
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		const int32 Count = MovieScene->GetSpawnableCount();
		if (Count > 0) {
			for (int32 Index = 0; Index <= Count - 1; Index++) {
				const FGuid Guid = MovieScene->GetSpawnable(Index).GetGuid();
				FString Name = MovieScene->GetObjectDisplayName(Guid).ToString();
				Result.Add(Index, Name);
			}
		}
	}
	return Result;
}

TMap<int32, FString> USmartMovieSceneSequenceExtensions::SmRenameSpawnableByData(UMovieSceneSequence * Sequence, TMap<int32, FString> Data, bool level)
{
	TMap<int32, FString> Result;

	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		const int32 Count = MovieScene->GetSpawnableCount();
		if (level) {
			for (TPair<int32, FString> tp : Data) {
				if (tp.Key + 1 <= Count) {
					const FGuid Guid = MovieScene->GetSpawnable(tp.Key).GetGuid();
					FMovieSceneSpawnable* msp = MovieScene->FindSpawnable(Guid);
					msp->SetName(tp.Value);
				}
				else {
					Result.Add(tp.Key, tp.Value);
				}
			}
		}
		else {
			for (TPair<int32, FString> tp : Data) {
				if (tp.Key + 1 <= Count) {
					const FGuid Guid = MovieScene->GetSpawnable(tp.Key).GetGuid();
					const FText NewName = FText::FromString(tp.Value);
					MovieScene->SetObjectDisplayName(Guid, NewName);
				}
				else {
					Result.Add(tp.Key, tp.Value);
				}
			}
		}
	}
	return Result;
}


bool USmartMovieSceneSequenceExtensions::SmRenameSpawnableByGuid(UMovieSceneSequence * Sequence, FString Name, FGuid Guid)
{

	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		FMovieSceneSpawnable* MovieSceneSpawnable = MovieScene->FindSpawnable(Guid);
		if (MovieSceneSpawnable) {
			MovieSceneSpawnable->SetName(Name);
			return true;
		}
	}
	return false;
}

bool USmartMovieSceneSequenceExtensions::SmRenameSpawnableByIndex(UMovieSceneSequence * Sequence, int32 Index, FString Name, bool level)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		const int32 Count = MovieScene->GetSpawnableCount();
		if (Index + 1 <= Count) {
			const FGuid Guid = MovieScene->GetSpawnable(Index).GetGuid();
			if (level) {
				FMovieSceneSpawnable* MovieSceneSpawnable = MovieScene->FindSpawnable(Guid);
				MovieSceneSpawnable->SetName(Name);
			}
			else {
				const FText NewName = FText::FromString(Name);
				MovieScene->SetObjectDisplayName(Guid, NewName);
			}
			return true;
		}
	}
	return false;
}

void USmartMovieSceneSequenceExtensions::SmRemoveAllSpawnable(UMovieSceneSequence * Sequence)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		const int32 Count = MovieScene->GetSpawnableCount();
		if (Count > 0) {
			TArray<FGuid> Guids;
			for (int32 Index = 0; Index <= Count - 1; Index++) {
				const FGuid Guid = MovieScene->GetSpawnable(Index).GetGuid();
				Guids.Add(FGuid(Guid));
			}
			if (Guids.Num() > 0) {
				for (FGuid Guid : Guids) {
					MovieScene->RemoveSpawnable(Guid);
				}
			}
		}
	}
}
void USmartMovieSceneSequenceExtensions::SmRemoveCameraCutTrack(UMovieSceneSequence * Sequence)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		MovieScene->RemoveCameraCutTrack();
	}
}
bool USmartMovieSceneSequenceExtensions::SmRemoveMasterTrack(UMovieSceneSequence * Sequence, UMovieSceneTrack * Track)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		return MovieScene->RemoveMasterTrack(*Track);
	}
	return false;
}
bool USmartMovieSceneSequenceExtensions::SmRemoveTrack(UMovieSceneSequence * Sequence, UMovieSceneTrack * Track)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		return MovieScene->RemoveTrack(*Track);
	}
	return false;
}

bool USmartMovieSceneSequenceExtensions::SmRemoveSpawnableByIndex(UMovieSceneSequence * Sequence, int32 Index)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		const int32 Count = MovieScene->GetSpawnableCount();
		if (Index + 1 <= Count) {
			const FGuid Guid = MovieScene->GetSpawnable(Index).GetGuid();
			return MovieScene->RemoveSpawnable(Guid);
		}
	}
	return false;
}

bool USmartMovieSceneSequenceExtensions::SmRemovePossessableByGuid(UMovieSceneSequence * Sequence, FGuid Guid) {
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		return MovieScene->RemovePossessable(Guid);
	}
	return false;
}

bool USmartMovieSceneSequenceExtensions::SmRemoveSpawnableByGuid(UMovieSceneSequence * Sequence, FGuid Guid)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		return MovieScene->RemoveSpawnable(Guid);
	}
	return false;
}

void USmartMovieSceneSequenceExtensions::SmSetRange(UMovieSceneSequence * Sequence, int32 Start, int32 End, bool view)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		const FFrameRate Rate = MovieScene->GetDisplayRate();
		const float StartFrame = Start / (float)Rate.Numerator;
		const float EndFrame = End / (float)Rate.Numerator;
		MovieScene->SetWorkingRange(StartFrame, EndFrame);
		if (view) {
			MovieScene->SetViewRange(StartFrame, EndFrame);
		}
		else {
			const FMovieSceneEditorData EditorData = MovieScene->GetEditorData();
			if (EditorData.ViewStart < StartFrame || EditorData.ViewEnd > EndFrame) {
				float ViewStart = EditorData.ViewStart;
				float ViewEnd = EditorData.ViewEnd;
				if (EditorData.ViewStart < StartFrame) {
					ViewStart = StartFrame;
				}
				if (EditorData.ViewEnd > EndFrame) {
					ViewEnd = EndFrame;
				}
				MovieScene->SetViewRange(ViewStart, ViewEnd);
			}
		}
	}
}

void USmartMovieSceneSequenceExtensions::SmSetPlaybackRange(UMovieSceneSequence * Sequence, int32 Start, int32 End)
{
	UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
	if (MovieScene) {
		FFrameNumber FrameNumber;
		const FFrameRate Resolution = MovieScene->GetTickResolution();
		const FFrameRate Rate = MovieScene->GetDisplayRate();
		FrameNumber.Value = Resolution.Numerator*Start / Rate.Numerator;
		MovieScene->SetPlaybackRange(FrameNumber, Resolution.Numerator*(End - Start + 1) / Rate.Numerator);
	}
}

TArray<int32> USmartMovieSceneSequenceExtensions::SmGetPlaybackRange(UMovieSceneSequence * Sequence)
{
	TArray<int32> FrameRange;
	const UMovieScene * MovieScene = Sequence->GetMovieScene();
	const FFrameRate DisplayRate = MovieScene->GetDisplayRate();
	const int32 StartFrame = ConvertFrameTime(MovieScene->GetPlaybackRange().GetLowerBoundValue(), MovieScene->GetTickResolution(), DisplayRate).FloorToFrame().Value;
	const int32 EndFrame = ConvertFrameTime(MovieScene->GetPlaybackRange().GetUpperBoundValue(), MovieScene->GetTickResolution(), DisplayRate).FloorToFrame().Value;
	FrameRange.Add(StartFrame);
	FrameRange.Add(EndFrame);
	return FrameRange;
}
